﻿using System;
using System.Security.Cryptography;
using System.Text;

namespace CodeContrib.Security
{
	public static class HmacExtensions
	{
		public static byte[] Key { get; set; } // This should be set to a different value per application.

		static HmacExtensions()
		{
			Key = new byte[]
				      {
					      1, 2, 3, 4, 5, 6, 7, 8
				      };
		}

		public static string GetExpiringHmac(this string value, DateTime expiryDate)
		{
			HMAC algorthm = new HMACSHA1(Key);

			try
			{
				var input = expiryDate.Ticks + value;

				var hashBytes = algorthm.ComputeHash(Encoding.UTF8.GetBytes(input));
				var result = new byte[8 + hashBytes.Length];

				hashBytes.CopyTo(result, 8);

				BitConverter.GetBytes(expiryDate.Ticks).CopyTo(result, 0);

				return Swap(Convert.ToBase64String(result), "+=/", "-_,");
			}
			finally
			{
				algorthm.Clear();
			}
		}

		private static string Swap(string str, string input, string output)
		{
			// Used to avoid any characters that aren't safe in URLs.
			for (var i = 0; i < input.Length; i++)
			{
				str = str.Replace(input[i], output[i]);
			}

			return str;
		}

		public static HmacResult VerifyExpiringHmac(this string value, string expiringHmac)
		{
			var bytes = Convert.FromBase64String(Swap(expiringHmac, "-_,", "+=/"));

			var claimedExpiry = new DateTime(BitConverter.ToInt64(bytes, 0));

			if (claimedExpiry < DateTime.Now)
			{
				return HmacResult.Expired;
			}

			if (expiringHmac == GetExpiringHmac(value, claimedExpiry))
			{
				return HmacResult.Valid;
			}

			return HmacResult.Invalid;
		}
	}
}